package pro2_transport;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/*
 以上采用NIO的分发模型 ，特点:
  1. 将一个完整处理过程分解为多个任务.
  2. 每个任务的动作不产生阻塞
  3. 在任务执行状态被 触发时，才会执行任务.通知 机制.

 */

/**
 * @program: netty
 * @description: NIO版服务器 (非阻塞）
 * @author: zy
 * @create: 2025-03-12 10:00
 */
public class P2_PlainNioServer {

    public static void main(String[] args) throws IOException {
        new P2_PlainNioServer().serve(9999);
    }

    public void serve(int port) throws IOException {
        // 打开一个 ServerSocketChannel，用来监听客户端连接
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);  // 配置为非阻塞模式（BIO -> NIO 的关键）

// 获取底层的 ServerSocket，并绑定端口
        ServerSocket ss = serverChannel.socket();
        InetSocketAddress address = new InetSocketAddress(port);
        ss.bind(address);

// 打开一个 Selector（多路复用器），用于监听多个通道的事件
        Selector selector = Selector.open();

// 将 serverChannel 注册到 Selector，监听 "接受新连接" (OP_ACCEPT) 事件
// 注意：ServerSocketChannel 只能监听 ACCEPT 事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);    //

// 准备要返回给客户端的消息
        final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());

// NIO 事件循环（死循环）
        for (;;) {
            try {
                // 阻塞，直到至少有一个事件就绪
                selector.select();
            } catch (IOException ex) {
                ex.printStackTrace();
                break;
            }

            // 拿到所有就绪的事件集合
            Set<SelectionKey> readyKeys = selector.selectedKeys();

            // 使用迭代器遍历所有就绪事件
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove(); // 必须手动移除，否则下次 select() 还会返回

                try {

                    // 1. 处理新连接（OP_ACCEPT）
                    if (key.isAcceptable()) {
                        // 获取服务端 Channel
                        ServerSocketChannel server =
                                (ServerSocketChannel) key.channel();
                        // 接收客户端连接（非阻塞，可能返回 null）
                        SocketChannel client = server.accept();
                        client.configureBlocking(false);  // 设置客户端为非阻塞
                        // 将新客户端注册到 Selector，监听 可读/可写 事件，并附加一份 msg
                        client.register(selector,
                                SelectionKey.OP_WRITE | SelectionKey.OP_READ,
                                msg.duplicate());
                        System.out.println("接收来自客户端: " + client + " 的连接");
                    }

                    // 2. 处理可读事件（OP_READ）
                    if (key.isReadable()) {
                        SocketChannel client =
                                (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int bytesRead = client.read(buffer);
                        if (bytesRead > 0) {
                            buffer.flip();
                            System.out.println("收到客户端消息: " +
                                    new String(buffer.array(), 0, bytesRead));
                        } else if (bytesRead == -1) {
                            // 客户端关闭连接
                           // client.close();
                        }
                    }

                    // 3. 处理可写事件（OP_WRITE）
                    if (key.isWritable()) {
                        SocketChannel client =
                                (SocketChannel) key.channel();
                        ByteBuffer buffer =
                                (ByteBuffer) key.attachment(); // 获取之前注册时附加的 msg
                        while (buffer.hasRemaining()) {
                            // 发送消息到客户端
                            if (client.write(buffer) == 0) {
                                break; // Channel 满了，下次再写
                            }
                        }
                        // 写完后关闭客户端（相当于短连接）
                        //client.close();
                    }

                } catch (IOException ex) {
                    // 出现异常时，取消 key 并关闭通道，避免资源泄漏
                    key.cancel();
                    try {
                        key.channel().close();
                    } catch (IOException cex) {
                        // 忽略关闭异常
                    }
                }
            }
        }

    }
}


